package dataccess

/*
   dao层，封装业务无关的mysql操作
*/
import (
	"database/sql"
	"sync"
	"time"

	"hermestrans/internal/common"

	"github.com/pkg/errors"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type Mysql struct {
	MysqlDB *gorm.DB
}

var (
	mysqlIns  *Mysql
	mysqlOnce sync.Once
)

func GetInstanceMysql() *Mysql {
	return mysqlIns
}

func NewMysql(msqlConf mysql.Config, gConfig gorm.Config, maxIdleConns, maxOpenConns int, connMaxIdleTime time.Duration) (*Mysql, error) {
	var (
		db    *gorm.DB
		sqlDB *sql.DB
		err   error
	)
	mysqlOnce.Do(func() {
		db, err = gorm.Open(mysql.New(msqlConf), &gConfig)
		if err != nil {
			err = errors.Wrap(err, "gorm open err")
			return
		}

		sqlDB, err = db.DB()
		if err != nil {
			err = errors.Wrap(err, "get db err")
			return
		}
		sqlDB.SetMaxIdleConns(maxIdleConns)
		sqlDB.SetMaxOpenConns(maxOpenConns)
		sqlDB.SetConnMaxIdleTime(connMaxIdleTime)
		mysqlIns = &Mysql{
			MysqlDB: db,
		}
	})
	return mysqlIns, err
}

/*
func (m *Mysql) Begin() Mysql {
	return Mysql{
		m.DB.Begin(),
	}
}

func (m *Mysql) Commit() Mysql {
	return Mysql{
		m.DB.Commit(),
	}
}

func (m *Mysql) Rollback() Mysql {
	return Mysql{
		m.DB.Rollback(),
	}
}*/

func (m *Mysql) Close() {
	m.Close()
}

func (m *Mysql) IdempotentInsert(tableName string, item interface{}, conds string) error {
	var (
		count int64
	)
	findTx := m.MysqlDB.Table(tableName).Where(conds).Count(&count)
	if findTx.Error != nil {
		return errors.WithMessage(findTx.Error, "find result fail")
	}
	if count != 0 {
		return nil
	}
	tx := m.MysqlDB.Table(tableName).Save(item)
	if tx.Error != nil {
		return errors.WithMessage(tx.Error, "save to db fail")
	}
	return nil
}

func (m *Mysql) Upsert(op string, tableName string, item interface{}, conds string) error {
	var (
		count int64
		tx    *gorm.DB
	)
	findTx := m.MysqlDB.Table(tableName).Where(conds).Count(&count)
	if findTx.Error != nil {
		return errors.WithMessage(findTx.Error, "find result fail")
	}

	if count == 0 {
		tx = m.MysqlDB.Table(tableName).Create(item)
		if tx.Error != nil {
			return errors.WithMessagef(tx.Error, "create record in table %s fail", tableName)
		}
	} else {
		if op == common.DBDelete {
			tx = m.MysqlDB.Table(tableName).Where(conds).Delete(item)
		} else {
			tx = m.MysqlDB.Table(tableName).Where(conds).Updates(item)
		}
		if tx.Error != nil {
			return errors.WithMessagef(tx.Error, "update record in table %s fail", tableName)
		}
	}
	return nil
}

type TransactionFunc func(tx *Mysql) error

func (m *Mysql) ExecuteWithTransaction(fn TransactionFunc) error {
	var err error
	tx := m.MysqlDB.Begin()
	if tx.Error != nil {
		return tx.Error
	}
	newTx := &Mysql{MysqlDB: tx}
	if err = fn(newTx); err != nil {
		if er := tx.Rollback().Error; er != nil {
			return errors.WithMessage(er, "roll back err")
		}
		return err
	}
	if err = tx.Commit().Error; err != nil {
		return errors.WithMessage(err, "commit err")
	}
	return err
}
